using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
#if WINDOWS
using Microsoft.Xna.Framework.Storage;
#endif
using SpriteGame.Rendering;
using SpriteGame.Scenes;
using SpriteGame.Helpers;
using SpriteGame.UI;
using FarseerPhysics.Collision;
using SpriteGame.Entities.Actors;

namespace SpriteGame
{
    public enum SceneIndex
    {
        MENU,
        MULTIPLAYER,
        SINGLEPLAYER,
        EDITOR
    }
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        SpriteFont font;

        //The desired output resolution
        int width = 1280;
        int height = 720;
        int viewportWidth;
        int viewportHeight;

        #region CameraAndProjections

        Vector2 lower;
        Vector2 upper;

        Vector2 viewCenter = new Vector2(0.0f, 20.0f);
        public Vector2 ViewCenter
        {
            get { return viewCenter; }
            set { viewCenter = value; }
        }

        float viewZoom = 1.9f;
        public float ViewZoom
        {
            get { return viewZoom; }
            set { viewZoom = value; }
        }
        //An orthographic view projection matching the screen bounds
        Matrix projection;
        //Matrix isoProjection;
        public Matrix Projection
        {
            get { return projection; }
        }

        #endregion

        //HACK HACK HACK
        Scene menu;
        Scene currentScene;

        Player player;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            //graphics.PreferMultiSampling = true;
            graphics.SynchronizeWithVerticalRetrace = true;

            Window.AllowUserResizing = true;
            //Window.ClientSizeChanged += new EventHandler(Window_ClientSizeChanged);
            Window.ClientSizeChanged += new EventHandler<EventArgs>(Window_ClientSizeChanged);
            graphics.PreferredBackBufferWidth = width;
            graphics.PreferredBackBufferHeight = height;

            IsMouseVisible = true;
            IsFixedTimeStep = true;
        }

        public void ChangeScene(SceneIndex _target, string _levelName)
        {
            currentScene = new ShooterGame(this);
            currentScene.LoadContent();
            /*
            Scene previousScene = currentScene;
            switch (_target)
            {
                case(SceneIndex.EDITOR):
                    currentScene = new LevelEdit(this);
                    break;
                case(SceneIndex.SINGLEPLAYER):
                    currentScene = new SinglePlayerGame(this, _levelName);
                    break;
                case(SceneIndex.MULTIPLAYER):
                    currentScene = new MultiplayerGame(this, _levelName);
                    break;
                default:
                    currentScene = menu;
                    break;
            }
            if (currentScene != previousScene)
            {
                currentScene.LoadContent();
            }*/
        }

        protected override void Initialize()
        {
            AudioManager audio = new AudioManager(this, Content);

            Components.Add(audio);

            TextureLibrary.InitTextureLibrary(Content);

            //HACK HACK HACK
            menu = new MainMenu(this);
            InputHandler.LoadInputHandler(this);
            base.Initialize();
        }

        protected override void LoadContent()
        {
            graphics.PreferredBackBufferWidth = width;
            graphics.PreferredBackBufferHeight = height;
            Resize(GraphicsDevice.PresentationParameters.BackBufferWidth,
       GraphicsDevice.PresentationParameters.BackBufferHeight);
            spriteBatch = new SpriteBatch(GraphicsDevice);

            font = Content.Load<SpriteFont>("Textures/Fonts/CourierNew");
            UIPanel.GlobalFont = font;

            //AudioManager.SoundManager.LoadSong("Menu", "Music/Menu");
            //AudioManager.SoundManager.LoadSong("GamePlay", "Music/GamePlay");
            AudioManager.SoundManager.LoadSound("BallBounce", "SFX/BallBounce");
            AudioManager.SoundManager.LoadSound("Explosion", "SFX/Explosion");

            ChangeScene(SceneIndex.MENU, "");

            WeaponHelper.Initialize(Content);

            player = new Player(Content.Load<Texture2D>(@"Textures\SpriteAnimations\Abe\StillBody"));
        }

        protected override void UnloadContent()
        {
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            InputHandler.UpdateInput();

            KeyboardState ks = Keyboard.GetState();

            bool dirtyView = false;

            if (currentScene == null)
            {
                currentScene = menu;
            }

            //START HACK HACK HACK
            #region Temporary keyboard commands
            if (ks.IsKeyDown(Keys.PageDown) || InputHandler.IsGamepadButtonHeld(Buttons.X))
            {
                viewZoom -= 0.01f;
                dirtyView = true;
            }
            if (ks.IsKeyDown(Keys.PageUp) || InputHandler.IsGamepadButtonHeld(Buttons.B))
            {
                viewZoom += 0.01f;
                dirtyView = true;
            }

            if (ks.IsKeyDown(Keys.End))
            {
                viewZoom = 1.0f;
                dirtyView = true;
            }

            if (ks.IsKeyDown(Keys.Left) || InputHandler.IsGamepadButtonHeld(Buttons.DPadLeft))
            {
                viewCenter.X -= 0.5f;
                dirtyView = true;
            }

            if (ks.IsKeyDown(Keys.Right) || InputHandler.IsGamepadButtonHeld(Buttons.DPadRight))
            {
                viewCenter.X += 0.5f;
                dirtyView = true;
            }

            if (ks.IsKeyDown(Keys.Up) || InputHandler.IsGamepadButtonHeld(Buttons.DPadUp))
            {
                viewCenter.Y += 0.5f;
                dirtyView = true;
            }

            if (ks.IsKeyDown(Keys.Down) || InputHandler.IsGamepadButtonHeld(Buttons.DPadDown))
            {
                viewCenter.Y -= 0.5f;
                dirtyView = true;
            }
            #endregion
            //END HACK HACK HACK

            //testImage.SetPosition(testImage.GetPosition() + Vector2.One);

            if (dirtyView)
            {
                Resize(GraphicsDevice.PresentationParameters.BackBufferWidth,
GraphicsDevice.PresentationParameters.BackBufferHeight);
                dirtyView = false;
            }

            currentScene.Update(gameTime);

            player.Update(gameTime);

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            //HACK HACK HACK
            currentScene.Draw(gameTime);
            spriteBatch.Begin();

            currentScene.DrawHUD(spriteBatch);

            //player.Draw(spriteBatch);

            spriteBatch.End();
            base.Draw(gameTime);
        }

        #region Window sizing and camera handlers

        void Window_ClientSizeChanged(object sender, EventArgs e)
        {
            viewportWidth = Window.ClientBounds.Width;
            viewportHeight = Window.ClientBounds.Height;
            graphics.PreferredBackBufferWidth = Window.ClientBounds.Width;
            graphics.PreferredBackBufferHeight = Window.ClientBounds.Height;
            Resize(GraphicsDevice.PresentationParameters.BackBufferWidth,
                GraphicsDevice.PresentationParameters.BackBufferHeight);

            currentScene.ScreenResized(viewportWidth, viewportHeight);
        }

        float screenRatio;
        float halfWidth, halfHeight;
 
        //Classes that handle window projection and sizeing
        private void Resize(int w, int h)
        {
            //int lastWidth = width;
            width = w;
            height = h;

            halfWidth = w / 2;
            halfHeight = h / 2;

            //viewZoom = viewZoom * (width / (float)lastWidth);

            viewportWidth = Window.ClientBounds.Width;
            viewportHeight = Window.ClientBounds.Height;

            screenRatio = viewportWidth / (float)viewportHeight;

            Vector2 extents = new Vector2(screenRatio * 25.0f, 25.0f);
            //extents *= viewZoom;

            lower = viewCenter - extents;
            upper = viewCenter + extents;

            float tWidth = upper.X - lower.X;
            float tHeight = upper.Y - lower.Y;

            Console.WriteLine(tWidth);
            Console.WriteLine(tHeight);

            //float halfWidth = viewportWidth * .1f;
            //float halfHeight = viewportHeight * .1f;

            projection = Matrix.CreateOrthographicOffCenter(lower.X, upper.X, lower.Y, upper.Y, -1, 5);
        }

        /// <summary>
        /// Returns a Vector2 representing the actual position of the mouse in world coordinages
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public Vector2 ConvertScreenToWorld(int x, int y)
        {
            float u = x / (float)viewportWidth;
            float v = (viewportHeight - y) / (float)viewportHeight;

            Vector2 p = new Vector2();
            p.X = (1.0f - u) * lower.X + u * upper.X;
            p.Y = (1.0f - v) * lower.Y + v * upper.Y;

            return p;
        }

        public Vector2 ConvertWorldToScreen(Vector2 _pos)
        {
            /*_pos.X = (_pos.X - lower.X) / (upper.X - lower.X);
            _pos.Y = (_pos.Y - lower.Y) / (upper.Y - lower.Y);

            _pos.X *= (float)viewportWidth;
            //_pos.Y *= (float)viewportHeight;

            return _pos;*/

            Vector2 pt = Vector2.Transform(Vector2.Zero, projection);
            //pt.Y *= -(height / 2);
            //pt.X *= -(width / 2);
            //pt.Y += halfHeight;
            //pt.X += halfWidth;
            return pt;
            //Vector3 pos =  GraphicsDevice.Viewport.Project(new Vector3(_pos.X, _pos.Y, 0.5f), projection, Matrix.Identity, Matrix.Identity);
            //pt.Y *= -1;
            //return new Vector2(pos.X, pos.Y);
        }

        public void ResetLerp()
        {
            //lerpFactor = 0.000001f;
        }

        public void SetViewCenter(Vector2 _target)
        {
            viewCenter = _target;
            Resize(GraphicsDevice.PresentationParameters.BackBufferWidth,
GraphicsDevice.PresentationParameters.BackBufferHeight);
        }

        public void LerpCamera(Vector2 _delta, float _speed)
        {
            //lerpFactor += _speed;
            //lerpFactor = MathHelper.Clamp(lerpFactor, 0.1f, 1.0f);
            viewCenter = Vector2.SmoothStep(viewCenter, _delta, _speed);
            Resize(GraphicsDevice.PresentationParameters.BackBufferWidth,
GraphicsDevice.PresentationParameters.BackBufferHeight);
        }

        public void ViewCenterAdd(Vector2 _delta)
        {
            viewCenter += _delta;
            Resize(GraphicsDevice.PresentationParameters.BackBufferWidth,
GraphicsDevice.PresentationParameters.BackBufferHeight);
        }

        internal void SetZoom(float p)
        {
            viewZoom = p;
            Resize(GraphicsDevice.PresentationParameters.BackBufferWidth,
GraphicsDevice.PresentationParameters.BackBufferHeight);
        }

        /// <summary>
        /// Returns an AABB that is the size of the viewport in world space
        /// </summary>
        /// <returns></returns>
        public AABB GetScreenBounds()
        {
            Vector2 min = ConvertScreenToWorld(0, 0);
            Vector2 max = ConvertScreenToWorld(viewportWidth, viewportHeight);
            return new AABB(ref min, ref max);
        }

        #endregion

    }
}
